﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using Bamboo.Core;
using NHibernate;
using NHibernate.Linq;
using System.Data;

namespace Bamboo.Infrastructure.Repository.NHibernate
{
    public class Repository : IRepository
    {
        static Repository()
        {
            HibernatingRhinos.NHibernate.Profiler.Appender.NHibernateProfiler.Initialize();
        }

        public Repository(ISession session)
        {
            Contract.Requires(session != null && session.IsConnected  && session.IsOpen);

            this.session = session;
        }

        protected readonly ISession session;

        #region IDisposable Members

        private bool disposed = false;

        public void Dispose()
        {
            if (!this.disposed)
            {
                this.session.Clear();

                this.session.Dispose();

                this.disposed = true;
            }
        }

        #endregion

        #region IRepository Members

        public TEntity Get<TEntity>(object id)
        {
            return this.session.Get<TEntity>(id); 
        }

        public IList<TEntity> Get<TEntity>(Expression<Func<TEntity, bool>> filter)
        {
            return this.session.Query<TEntity>().Where(filter).ToList();
        }

        public IQueryable<TEntity> Query<TEntity>(params string[] prefetchProperties)
        {
            var query = this.session.Query<TEntity>();
            
            foreach (var property in prefetchProperties)
            {
                Expression param = Expression.Parameter(typeof(TEntity), "e");
                Expression proper = Expression.Property(param, property);
                Expression<Func<TEntity,object>> expr = Expression.Lambda<Func<TEntity,object>>(proper);
                query.Fetch(expr);
            }

            return query;
        }

        public object Add(object entity)
        {
            using (var transaction = this.session.BeginTransaction())
            {
                var id = this.session.Save(entity);

                transaction.Commit();

                return id;
            }
        }

        public void Update(object entity)
        {
            using (var transaction = this.session.BeginTransaction())
            {
                if (this.session.Contains(entity))
                {
                    entity = this.session.Merge(entity);
                }

                this.session.Update(entity);

                transaction.Commit();
            }
        }

        public void Remove(object entity)
        {
            using (var transaction = this.session.BeginTransaction())
            {
                this.session.Delete(entity);

                transaction.Commit();
            }
        }

        public void Refresh(object entity)
        {
            this.session.Refresh(entity);
        }

        public void Stale(object entity)
        {
            this.session.Evict(entity);
        }

        public void AddOrUpdate(object entity)
        {
            using (var transaction = this.session.BeginTransaction())
            {
                this.session.SaveOrUpdate(entity);

                transaction.Commit();
            }
        }

        public void ExecuteCommand(string command,params object[] parametter)
        {
            IDbCommand dbcommand = this.session.Connection.CreateCommand();
            dbcommand.CommandText = command;
            if (parametter != null)
            {
                parametter.ForEach((e) =>
                {
                    dbcommand.Parameters.Add(e);
                });
            }
            dbcommand.ExecuteNonQuery();
        }

        public IUnitOfWork CreateUnitOfWork()
        {
            return new UnitOfWork(this.session);
        }

        #endregion
    }
}
